#include "evaluate.h"

extern int gridUnit;
extern int x_gridNum;
extern int y_gridNum;
extern int numBins;
extern ckt this_ckt;
extern double HPWL, penalty, displacement, max_displacement, targUt;
extern string directory;
extern vector<double> util_array;


/*-------------------------------------------------------*/
/*  Desc:   A function to read bookshelf format ckt  */
/*-------------------------------------------------------*/
int read_bookshelf(ckt & ptrckt, int argc, char** argv)
{
	cout << "Reading bookshelf format .." <<endl;
	ptrckt.benchmark.assign(argv[1]); // The address of the .aux file
	size_t found=ptrckt.benchmark.find_last_of("/\\");
	if(found==ptrckt.benchmark.npos) //If we cannot find a '/' in the address
	{
		directory="./";  // The .aux file is in the current folder
		ptrckt.benchmark = ptrckt.benchmark.substr(0, ptrckt.benchmark.find_last_of(".")); 
	}
	else  // The .aux file not in the current folder
	{
		directory = ptrckt.benchmark.substr(0,found);
		directory += "/";
		ptrckt.benchmark=ptrckt.benchmark.substr(found+1, ptrckt.benchmark.find_last_of(".")-(found+1));
		// Extract the name of the circuit
	}
	cout << "  benchmark name : "<< ptrckt.benchmark <<endl;
	cout << "  directory      : "<< directory <<endl;
	cout << "  .aux file      : "<< argv[1] <<endl;

	/* parsing .aux file */
	string auxStr[6];
	ifstream dot_aux(argv[1]); // Open the .aux file
	if(!dot_aux.good())
	{
		cout << argv[1] << " does not exist. exiting .." <<endl;
		exit(1);
	}

	string tmpStr;
	dot_aux >> tmpStr >> tmpStr;
	while(!dot_aux.eof())
	{
		dot_aux >> tmpStr;
		string ext=tmpStr.substr(tmpStr.find_last_of(".")); // Extract the suffix
		if(ext == ".nodes")
			auxStr[0]=directory + tmpStr;
		else if(ext == ".nets")
			auxStr[1]=directory + tmpStr;
		else if(ext == ".pl")
			auxStr[2]=directory + tmpStr;
		else if(ext == ".wts")
			auxStr[3]=directory + tmpStr;
		else if(ext == ".scl")
			auxStr[4]=directory + tmpStr;
		else if(ext == ".shapes")
			auxStr[5]=directory + tmpStr;
		else
			continue;
	}



	/* parsing .nodes file */
	ifstream dot_nodes(auxStr[0].c_str());
	cout << "  .nodes file    : "<< auxStr[0] <<endl;

	if(!dot_nodes.good())
	{
		cout << auxStr[0] << " does not exist. exiting .." <<endl;
		exit(1);
	}

	do {
		dot_nodes >> tmpStr;
	} while(tmpStr!="NumNodes");

	unsigned tmpInteger;
	dot_nodes >> tmpStr >> tmpInteger;
	dot_nodes >> tmpStr >> tmpStr >> tmpStr;
	ptrckt.nodes.resize(tmpInteger);
	ptrckt.total_mArea = ptrckt.total_fArea = 0.0;

	bool isMovable = false;
	for(unsigned i = 0; i < ptrckt.nodes.size(); i++)
	{
		if(!isMovable)
			dot_nodes >> tmpStr;
		ptrckt.nodes[i].obj_name = tmpStr; // Read in node name
		ptrckt.nodes[i].ID = i; // Set ID
		ptrckt.name2id.insert( pair<string,unsigned>(tmpStr, i)); // Map node name to ID
		dot_nodes >> ptrckt.nodes[i].width >> ptrckt.nodes[i].height >> tmpStr;

		if(tmpStr=="terminal")
		{
			isMovable=false;
			ptrckt.nodes[i].isFixed=true;
			ptrckt.nodes[i].isNI=false;
		}
		else if(tmpStr=="terminal_NI")
		{
			isMovable=false;
			ptrckt.nodes[i].isFixed=true;
			ptrckt.nodes[i].isNI=true;
		}
		else
		{
			isMovable=true;
			ptrckt.nodes[i].isFixed=false;
			ptrckt.nodes[i].isNI=false;
			ptrckt.total_mArea+=ptrckt.nodes[i].width*ptrckt.nodes[i].height;
		}
	}
	dot_nodes.close();



	/* parsing .nets file */
	ifstream dot_nets(auxStr[1].c_str());
	cout << "  .nets file     : "<< auxStr[1] <<endl;

	do {
		dot_nets >> tmpStr;
	} while(tmpStr != "NumNets");

	dot_nets >> tmpStr >> tmpInteger;
	dot_nets >> tmpStr >> tmpStr >> tmpStr;
	ptrckt.nets.resize(tmpInteger);

	do {
		getline(dot_nets, tmpStr);
	} while(tmpStr.find_first_of("NetDegree") == tmpStr.npos);

	for(unsigned i = 0; i < ptrckt.nets.size(); i++)
	{
		stringstream curLine(tmpStr);
		unsigned netDegree;
		curLine >> tmpStr >> tmpStr >> netDegree >> ptrckt.nets[i].net_name;
		ptrckt.nets[i].nodeIds.resize(netDegree);
		ptrckt.nets[i].x_offset.resize(netDegree);
		ptrckt.nets[i].y_offset.resize(netDegree);
		for(unsigned j = 0; j < netDegree; j++)
		{
			getline(dot_nets, tmpStr);
			stringstream curLine2(tmpStr);
			double x_offset, y_offset;
			string pinOwner;
			curLine2 >> pinOwner >> tmpStr >> tmpStr;
			if(tmpStr == ":")
				curLine2 >> x_offset >> y_offset;
			else
				x_offset = y_offset = 0.0;

			ptrckt.nets[i].x_offset[j] = x_offset;
			ptrckt.nets[i].y_offset[j] = y_offset;

			map<string,unsigned>::iterator it = ptrckt.name2id.find(pinOwner);
			if(it == ptrckt.name2id.end())
			{
				cout << "can't find a node "<< pinOwner << ". existing ..";
				exit(1);
			}
			ptrckt.nets[i].nodeIds[j] = it->second; // Save node information into net structure
			// ptrckt.nodes[it->second].node2nets.push_back(i); // Save net information into node structure
			pin temp = {x_offset, y_offset, i};
			ptrckt.nodes[it->second].pins.push_back(temp);
		}
		getline(dot_nets, tmpStr);
	}
	dot_nets.close();

	/* parsing .pl file */
	ifstream dot_pl(auxStr[2].c_str());
	cout << "  .pl file       : "<< auxStr[2] <<endl;
	if(!dot_pl.good())
	{
		cout << auxStr[2] << " does not exist. exiting .." <<endl;
		exit(1);
	}

	do {
		dot_pl >> tmpStr;
	} while(ptrckt.name2id.find(tmpStr) == ptrckt.name2id.end());

	isMovable=true;
	for(unsigned i = 0;i < ptrckt.nodes.size();i++)
	{
		if(!isMovable)
			dot_pl >> tmpStr;

		map<string, unsigned>::iterator it=ptrckt.name2id.find(tmpStr);
		if(it==ptrckt.name2id.end())
		{
			cout << "can't find a node " << tmpStr <<". existing .." <<endl;
			exit(1);
		}
		dot_pl >> ptrckt.nodes[it->second].x_coord >> ptrckt.nodes[it->second].y_coord >> tmpStr >> tmpStr;
		dot_pl >> tmpStr;
		if(tmpStr=="/FIXED" || tmpStr=="/FIXED_NI")
			isMovable=false;
		else
			isMovable=true;
	}
	dot_pl.close();

	/* parsing .scl file */
	ifstream dot_scl(auxStr[4].c_str());
	cout << "  .scl file      : "<< auxStr[4] <<endl;
	if(!dot_scl.good())
	{
		cout << auxStr[4] << " does not exist. exiting .."<<endl;
		exit(1);
	}

	do {
		dot_scl >> tmpStr;
	} while(tmpStr != "NumRows");
	dot_scl >> tmpStr >> ptrckt.numRows;
	ptrckt.rows.resize(ptrckt.numRows);

	bool irregular=false;
	ptrckt.designArea=0.;
	for(unsigned i=0;i<ptrckt.numRows;i++)
	{
		dot_scl >> tmpStr >> tmpStr;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].coordinate;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].height;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].sitewidth;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].sitespacing;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].siteorient;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].sitesymmetry;
		dot_scl >> tmpStr >> tmpStr >> ptrckt.rows[i].subrowOrigin >> tmpStr >> tmpStr >> ptrckt.rows[i].numSites;
		dot_scl >> tmpStr;

		ptrckt.rows[i].rx=ptrckt.rows[i].subrowOrigin+ptrckt.rows[i].sitespacing*ptrckt.rows[i].numSites;
		ptrckt.designArea+=ptrckt.rows[i].sitespacing*ptrckt.rows[i].numSites*ptrckt.rows[i].height;

		if(i==0)
		{
			ptrckt.lx=ptrckt.rows[0].subrowOrigin;
			ptrckt.rx=ptrckt.rows[0].rx;
			ptrckt.by=ptrckt.rows[0].coordinate;
		}
		else
		{
			if(ptrckt.lx > ptrckt.rows[i].subrowOrigin)
			{
				ptrckt.lx=ptrckt.rows[i].subrowOrigin;
				irregular=true;
			}
			if(ptrckt.rx < ptrckt.rows[i].rx)
			{
				ptrckt.rx=ptrckt.rows[i].rx;
				irregular=true;
			}
		}
	}
	dot_scl.close();

	/* populating row blockage list, ptrckt.blks */
	if(irregular)
	{
#ifdef DEBUG
		cout << "  -- placement row blockage exists .. " <<endl;
#endif
		for(unsigned i=0 ; i<ptrckt.numRows ; i++)
		{
			if(ptrckt.rows[i].subrowOrigin > ptrckt.lx) 
			{
				double x_coord=ptrckt.lx;
				double blkWidth=ptrckt.rows[i].subrowOrigin-ptrckt.lx;
				node tmpNode;
				tmpNode.x_coord=x_coord;
				tmpNode.y_coord=ptrckt.rows[i].coordinate;
				tmpNode.obj_name="PL_BLK";
				tmpNode.width=blkWidth;
				tmpNode.height=ptrckt.rows[i].height;
				tmpNode.isFixed=true;
				ptrckt.blks.push_back(tmpNode);
			}
			if(ptrckt.rows[i].rx < ptrckt.rx)
			{
				double x_coord=ptrckt.rows[i].rx;
				double blkWidth=ptrckt.rx-ptrckt.rows[i].rx;
				node tmpNode;
				tmpNode.x_coord=x_coord;
				tmpNode.y_coord=ptrckt.rows[i].coordinate;
				tmpNode.obj_name="PL_BLK";
				tmpNode.width=blkWidth;
				tmpNode.height=ptrckt.rows[i].height;
				tmpNode.isFixed=true;
				ptrckt.blks.push_back(tmpNode);
			}
		}
	}
	ptrckt.rowHeight=ptrckt.rows[0].height;
	ptrckt.ty=ptrckt.rows[ptrckt.numRows-1].coordinate+ptrckt.rowHeight;

	/* parsing .shapes file */
	/* populating shapes list, ptrckt.shapes */
	if(auxStr[5] != "")
	{
		ifstream dot_shapes(auxStr[5].c_str());
		cout << "  .shapes file    : "<< auxStr[5] <<endl;
		if(!dot_shapes.good())
		{
			cout << auxStr[5] << " does not exist. exiting .." <<endl;
			exit(1);
		}
		do {
			dot_shapes >> tmpStr;
		}	while(tmpStr!="NumNonRectangularNodes");

		unsigned numNonrects;
		dot_shapes >> tmpStr  >> numNonrects;

		for(unsigned i=0; i<numNonrects;i++)
		{
			unsigned numShapes;
			string nonrectNode;
			dot_shapes >> nonrectNode >> tmpStr >> numShapes;
			map<string,unsigned>::iterator it=ptrckt.name2id.find(nonrectNode);
			if(it==ptrckt.name2id.end())
			{
				cout << "can't find the node "<< tmpStr <<". exiting .."<<endl;
				exit(1);
			}

			double pre_x_coord, pre_y_coord;
			double pre_rectWidth, pre_rectHeight;
			dot_shapes >> tmpStr >> pre_x_coord >> pre_y_coord >> pre_rectWidth >> pre_rectHeight;
			for(unsigned j=1;j<numShapes;j++)
			{
				double x_coord, y_coord;
				double rectWidth, rectHeight;
				dot_shapes >> tmpStr >> x_coord >> y_coord >> rectWidth >> rectHeight;
				if(x_coord == pre_x_coord && rectWidth == pre_rectWidth && j!=numShapes-1)
					pre_rectHeight+=rectHeight;
				else
				{
					node tmpNode;
					tmpNode.x_coord=pre_x_coord;
					tmpNode.y_coord=pre_y_coord;
					tmpNode.obj_name="SHAPE";
					tmpNode.width=pre_rectWidth;
					tmpNode.height=pre_rectHeight;
					tmpNode.isFixed=true;

					ptrckt.nodes[it->second].shapeIds.push_back(ptrckt.shapes.size());
					ptrckt.shapes.push_back(tmpNode);

					pre_x_coord=x_coord;
					pre_y_coord=y_coord;
					pre_rectWidth=rectWidth;
					pre_rectHeight=rectHeight;
				}
			}
			node tmpNode;
			tmpNode.x_coord=pre_x_coord;
			tmpNode.y_coord=pre_y_coord;
			tmpNode.obj_name="SHAPE";
			tmpNode.width=pre_rectWidth;
			tmpNode.height=pre_rectHeight;
			tmpNode.isFixed=true;

			ptrckt.nodes[it->second].shapeIds.push_back(ptrckt.shapes.size());
			ptrckt.shapes.push_back(tmpNode);
		}
	}
	
	/* calculate total fixed area */
	for(unsigned i = 0; i < ptrckt.nodes.size(); i++)
	{
		/* terminal NIs are excluated */
		if(!ptrckt.nodes[i].isFixed || ptrckt.nodes[i].isNI)
			continue;
		if(ptrckt.nodes[i].shapeIds.size() == 0 )
			ptrckt.total_fArea+=ptrckt.nodes[i].width*ptrckt.nodes[i].height;
		else
			for(unsigned j=0;j<ptrckt.nodes[i].shapeIds.size();j++)
			{
				unsigned k=ptrckt.nodes[i].shapeIds[j];
				ptrckt.total_fArea+=ptrckt.shapes[k].width*ptrckt.shapes[k].height;
			}
	}


#ifdef DEBUG
	for(unsigned i=0;i<ptrckt.blks.size(); i++)
		cout << ptrckt.blks[i].x_coord << " " << ptrckt.blks[i].y_coord << " " << ptrckt.blks[i].width<< " " << ptrckt.blks[i].height <<endl;
#endif

  cout << "---------------------------------------------------------------" <<endl;
	cout <<"  total nodes    : " << ptrckt.nodes.size() <<endl;
	cout <<"  total nets     : " << ptrckt.nets.size() <<endl;;
	cout <<"  design area    : " << ptrckt.designArea <<endl;
	cout <<"  total f_area   : " << ptrckt.total_fArea << endl;
	cout <<"  total m_area   : " << ptrckt.total_mArea << endl;
	if(ptrckt.designArea-ptrckt.total_fArea > 1.0e-5)
	cout <<"  design util    : " << ptrckt.total_mArea/(ptrckt.designArea-ptrckt.total_fArea) <<endl;
	cout <<"  num rows       : " << ptrckt.numRows <<endl;
	cout <<"  row height     : " << ptrckt.rowHeight <<endl;
  cout << "---------------------------------------------------------------" <<endl;

	/* parsing .legal file */
	ifstream dot_legal(argv[2]);
	cout << "  .legal file    : "<< argv[2] <<endl;
	if(!dot_legal.good())
	{
		cout << argv[2] << " does not exist. exiting .." <<endl;
		exit(1);
	}

	// Skip irrelevent information till the first node name (is it robust?)
	do {
		dot_legal >> tmpStr;
	} while(ptrckt.name2id.find(tmpStr) == ptrckt.name2id.end());

	isMovable = true;
	for(unsigned i = 0; i < ptrckt.nodes.size(); i++)
	{
		if(!isMovable)
		{
			dot_legal >> tmpStr;
		} // Skip all movable nodes

		map<string, unsigned>::iterator it = ptrckt.name2id.find(tmpStr);
		if(it == ptrckt.name2id.end())
		{
			cout << "can't find a node " << tmpStr <<". existing .." <<endl;
			exit(1);
		}

		if(!ptrckt.nodes[it->second].isFixed)
		{
			dot_legal >> ptrckt.nodes[it->second].x_coord >> ptrckt.nodes[it->second].y_coord >> tmpStr >> tmpStr;
		}
		else
		{
			dot_legal >> tmpStr >> tmpStr >> tmpStr >> tmpStr;
		}

		ptrckt.nodes[it->second].initial_x = ptrckt.nodes[it->second].x_coord;
		ptrckt.nodes[it->second].initial_y = ptrckt.nodes[it->second].y_coord;
		
		dot_legal >> tmpStr;
		
		if(tmpStr=="/FIXED" || tmpStr=="/FIXED_NI")
			isMovable=false;
		else
			isMovable=true;
		// .legal file parsing does not distinguish TERMINAL and TERMINAL_NI nodes
	}
	dot_legal.close();
	
	cout << "Parsing Finished!" << endl;

	gridUnit = BIN_DIM * ptrckt.rowHeight;
	x_gridNum = (int) ceil((double)(ptrckt.rx - ptrckt.lx) / gridUnit);
	y_gridNum = (int) ceil((double)(ptrckt.ty - ptrckt.by) / gridUnit);
	numBins = x_gridNum * y_gridNum;

	return 1;
}

